package com.qfedu.edu.seckill.filter;

import com.alibaba.fastjson.JSON;
import com.qfedu.edu.seckill.bo.SecurityUser;
import com.qfedu.edu.seckill.manager.RedisManager;
import com.qfedu.edu.seckill.result.R;
import com.qfedu.edu.seckill.result.ResponseCode;
import com.qfedu.edu.seckill.utils.HttpResponseUtils;
import com.qfedu.edu.seckill.vo.LoginVo;
import lombok.extern.slf4j.Slf4j;
import org.qfedu.edu.seckill.service.ITokenService;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 定制的这个登录的过滤器
 */
@Slf4j
public class LoginFilter extends UsernamePasswordAuthenticationFilter {

    //这个是登录的管理器(这个参数是从SecurityConfig中传递过来的)
    private AuthenticationManager authenticationManager;

    private ITokenService tokenService;

    private RedisManager redisManager;

    public LoginFilter(AuthenticationManager authenticationManager,ITokenService tokenService,RedisManager redisManager){
         this.authenticationManager=authenticationManager;
         this.tokenService=tokenService;
         this.redisManager=redisManager;
        this.setRequiresAuthenticationRequestMatcher(
                new AntPathRequestMatcher("/aclservice/user/login", HttpMethod.POST.name()));
    }


    /**
     * 获取前端请求的数据  然后封装成指定对象进行登录
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        //获取前端请求的数据
        LoginVo loginVo=getReqUsernameAndPassword(request);
        //将前端请求的数据 封装成 指定对象
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(JSON.toJSONString(loginVo), loginVo.getPassword());
        //调用框架中的方法实现登录
        return authenticationManager.authenticate(usernamePasswordAuthenticationToken);
    }

    /**
     * 获取前端登录的数据
     * @param request
     * @return
     */
    private LoginVo getReqUsernameAndPassword(HttpServletRequest request) {
        //前端传递的是 json格式
        //json格式如何获取数据呢
        ServletInputStream inputStream = null;
        try {
            inputStream = request.getInputStream();
            byte[] buf=new byte[1024];
            int readLength = inputStream.read(buf);
            //接下来将读取到的数据 弄成字符串
            String jsonStr = new String(buf, 0, readLength);
            //将这个json格式的字符串 直接转换成Java对象
            return JSON.parseObject(jsonStr, LoginVo.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 登录成功的回调函数
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        log.info("登录成功....");
        //用户信息从哪里来呀?
        SecurityUser securityUser= (SecurityUser) authResult.getPrincipal();
        //首先生成token
        String token = tokenService.createToken(securityUser.getUser().getUserId(), securityUser.getUser().getUsername());
        log.info("生成的token信息是:{}",token);
        //接下来取出权限信息和角色信息
        List<String> roleAndPermStringList = securityUser.getRoleAndPermStringList();
        //接下来就可以将这个权限信息和角色信息存储到第三方设备中了...(只能存储到Redis中)
        //这里采用的是String类型  key:token  val:权限和角色的JSON格式
        redisManager.addString(token,JSON.toJSONString(roleAndPermStringList),30, TimeUnit.DAYS);
        log.info("登录信息存储到Redis中成功....");
        //将信息返回给客户端
        HttpResponseUtils.sendResponse(response, R.ok()
                .data("token",token)
                .data("userId",securityUser.getUser().getUserId())
                .data("username",securityUser.getUser().getUsername()));
    }

    /**
     * 登录失败的回调函数
     * @param request
     * @param response
     * @param failed
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        log.info("登录失败");
        HttpResponseUtils.sendResponse(response,R.error(ResponseCode.LOGIN_EXCEPTION));
    }
}
